//
//  ContestEntry.m
//  My Dream Slapper
//
//  Created by Bob Newhart on 8/23/06.
//  Copyright (c) 2006, Jason Harris and Geekspiff
//  All rights reserved.
//  
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided
//  that the following conditions are met:
//  
//  Redistributions of source code must retain the above copyright notice, this list of conditions and the
//  following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list
//  of conditions and the following disclaimer in the documentation and/or other materials provided with the
//  distribution. Neither the name of Jason Harris, Geekspiff, nor the names of its contributors may be used
//  to endorse or promote products derived from this software without specific prior written permission.
//  
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
//  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
//  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
//  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//  POSSIBILITY OF SUCH DAMAGE.
//

#import "ContestEntry.h"
#import "MyManagedObjectContext.h"


@implementation ContestEntry

+ (void)initialize
{
	[self setKeys: [NSArray arrayWithObject: @"ratings"] triggerChangeNotificationsForDependentKey: @"isRated"];
	[self setKeys: [NSArray arrayWithObject: @"ratings"] triggerChangeNotificationsForDependentKey: @"isGloballyRated"];
}


+ (NSImage *)_unreadImage
{
	static NSImage *sImage = nil;
	if (!sImage)
	{
		NSString *path = [[NSBundle mainBundle] pathForResource: @"unread" ofType: @"tiff"];
		sImage = [[NSImage alloc] initWithContentsOfFile: path];
		NSParameterAssert( sImage != nil );
	}
	return sImage;
}


- (void)_addRatingsObservers
{
	NSSet *ratings = [self valueForKey: @"ratings"];
	NSEnumerator *ratingEnumerator = [ratings objectEnumerator];
	NSManagedObject *rating;
	
	while (rating = [ratingEnumerator nextObject])
	{
		[rating addObserver: self forKeyPath: @"rating" options: NSKeyValueObservingOptionNew context: @"rating"];
	}
	_registeredRatings = YES;
}


- (void)_removeRatingsObservers
{
	NSSet *ratings = [self valueForKey: @"ratings"];
	NSEnumerator *ratingEnumerator = [ratings objectEnumerator];
	NSManagedObject *rating;
	
	while (rating = [ratingEnumerator nextObject])
	{
		[rating removeObserver: self forKeyPath: @"rating"];
	}
	_registeredRatings = NO;
}


- (void)didTurnIntoFault
{
	[super didTurnIntoFault];
	[self _removeRatingsObservers];
}


- (NSImage *)readImage
{
	if (_validating) return nil;
    NSImage *image;
    
    [self willAccessValueForKey: @"readImage"];
    image = [self primitiveValueForKey: @"readImage"];
    if ( nil == image )
    {
        NSNumber *readObject = [self valueForKey: @"read"];
		BOOL read = readObject && [readObject boolValue];
		if (!read)
			image = [[self class] _unreadImage];
        [self setPrimitiveValue: image forKey: @"readImage"];
    }
    [self didAccessValueForKey: @"readImage"];
    return image;
}


- (void)setRead: (NSNumber *)read
{
	[self willChangeValueForKey: @"read"];
	[self setPrimitiveValue: read forKey: @"read"];
	[self didChangeValueForKey: @"read"];
	
	[self willChangeValueForKey: @"readImage"];
	[self setPrimitiveValue: nil forKey: @"readImage"];
	[self didChangeValueForKey: @"readImage"];
}


- (NSData *)rtfAbout
{
	if (_validating) return nil;
    NSData *data;
    
    [self willAccessValueForKey: @"rtfAbout"];
    data = [self primitiveValueForKey: @"rtfAbout"];
    if ( nil == data )
    {
        NSString *string = [self valueForKey: @"about"];
        NSAttributedString *as = [[[NSAttributedString alloc] initWithString: string] autorelease];
        data = [as RTFFromRange: NSMakeRange(0, [string length]) documentAttributes: nil];
        [self setPrimitiveValue: data forKey: @"rtfAbout"];
    }
    [self didAccessValueForKey: @"rtfAbout"];
    return data;
}


- (NSData *)rtfIdea
{
	if (_validating) return nil;
    NSData *data;
    
    [self willAccessValueForKey: @"rtfIdea"];
    data = [self primitiveValueForKey: @"rtfIdea"];
    if ( nil == data )
    {
        NSString *string = [self valueForKey: @"idea"];
        NSAttributedString *as = [[[NSAttributedString alloc] initWithString: string] autorelease];
        data = [as RTFFromRange: NSMakeRange(0, [string length]) documentAttributes: nil];
        [self setPrimitiveValue: data forKey: @"rtfIdea"];
    }
    [self didAccessValueForKey: @"rtfIdea"];
    return data;
}


- (NSManagedObject *)myRating
{
	NSString *username = [[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey: @"LastValidSQLUsername"];
	if ( ! username ) return nil;
	
	NSEnumerator *ratingEnumerator = [[self valueForKey: @"ratings"] objectEnumerator];
	NSManagedObject *rating;
	
	while (rating = [ratingEnumerator nextObject])
	{
		NSManagedObject *sqlUser = [rating valueForKey: @"sqlUser"];
		NSString *thisUsername = [sqlUser valueForKey: @"sqlUsername"];
		NSParameterAssert( thisUsername != nil );
		if ( [thisUsername isEqualToString: username] )
			return rating;
	}
	return nil;
}


- (NSManagedObject *)_createRating
{
	MyManagedObjectContext *moc = (MyManagedObjectContext *)[self managedObjectContext];
	NSManagedObject *ratingObject = [NSEntityDescription insertNewObjectForEntityForName: @"Rating" inManagedObjectContext: moc];
	
	[ratingObject setValue: [NSNumber numberWithInt: -1] forKey: @"id"];
	[ratingObject setValue: [moc mySQLUser] forKey: @"sqlUser"];
	
	[[self mutableSetValueForKey: @"ratings"] addObject: ratingObject];
	
	return ratingObject;
}


- (NSNumber *)rating
{
	if ( ! _registeredRatings )
		[self _addRatingsObservers];
	
	NSNumber *rating;
	
	[self willAccessValueForKey: @"rating"];
	NSManagedObject *myRating = [self myRating];
	if ( myRating )
		rating = [myRating valueForKey: @"rating"];
	else
		rating = [NSNumber numberWithInt: 0];
	[self didAccessValueForKey: @"rating"];
	
	return rating;
}


- (void)setRating: (NSNumber *)rating
{
	if ( ! _registeredRatings )
		[self _addRatingsObservers];
	
	[self willChangeValueForKey: @"rating"];
	MyManagedObjectContext *moc = (MyManagedObjectContext *)[self managedObjectContext];
	NSManagedObject *ratingObject = [self myRating];
	if ( ! ratingObject )
		ratingObject = [self _createRating];
	[ratingObject setValue: rating forKey: @"rating"];
	[moc markObjectAsDirty: ratingObject];
	[self didChangeValueForKey: @"rating"];

	[self willChangeValueForKey: @"isRated"];
	[self setPrimitiveValue: nil forKey: @"isRated"];
	[self didChangeValueForKey: @"isRated"];
	
	[self willChangeValueForKey: @"isGloballyRated"];
	[self setPrimitiveValue: nil forKey: @"isGloballyRated"];
	[self didChangeValueForKey: @"isGloballyRated"];
}


- (void)setRatings: (NSMutableSet *)ratings
{
	[self _removeRatingsObservers];
	[self willChangeValueForKey: @"ratings"];
	[self setPrimitiveValue: ratings forKey: @"ratings"];
	[self didChangeValueForKey: @"ratings"];
	[self _addRatingsObservers];
}


- (void)addRatingsObject: (id)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
    [self willChangeValueForKey:@"ratings" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
    [[self primitiveValueForKey: @"ratings"] addObject: value];
    [self didChangeValueForKey:@"ratings" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
    [changedObjects release];

	[value addObserver: self forKeyPath: @"rating" options: NSKeyValueObservingOptionNew context: @"rating"];
}


- (void)removeRatingsObject: (id)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
    [self willChangeValueForKey:@"ratings" withSetMutation:NSKeyValueMinusSetMutation usingObjects:changedObjects];
    [[self primitiveValueForKey: @"ratings"] removeObject: value];
    [self didChangeValueForKey:@"ratings" withSetMutation:NSKeyValueMinusSetMutation usingObjects:changedObjects];
    [changedObjects release];

	[value removeObserver: self forKeyPath: @"rating"];
}


- (NSNumber *)isRated
{
	if (_validating) return nil;
    NSNumber *isRated;
    
    [self willAccessValueForKey: @"isRated"];
    isRated = [self primitiveValueForKey: @"isRated"];
    if ( nil == isRated )
    {
		NSManagedObject *ratingObject = [self myRating];
		NSNumber *rating = [ratingObject valueForKey: @"rating"];
		isRated = [NSNumber numberWithBool: rating && ([rating intValue] != 0)];
		[self setPrimitiveValue: isRated forKey: @"isRated"];
    }
	[self didAccessValueForKey: @"isRated"];
	return isRated;
}


- (NSNumber *)isGloballyRated
{
	if (_validating) return nil;
    NSNumber *isGloballyRated;
    
    [self willAccessValueForKey: @"isGloballyRated"];
    isGloballyRated = [self primitiveValueForKey: @"isGloballyRated"];
    if ( nil == isGloballyRated )
    {
		BOOL foundRating = NO;
		NSEnumerator *ratingEnumerator = [[self valueForKey: @"ratings"] objectEnumerator];
		NSManagedObject *rating;
		
		while (rating = [ratingEnumerator nextObject])
		{
			if ( [[rating valueForKey: @"rating"] intValue] != 0 )
			{
				foundRating = YES;
				break;
				
			}
		}
		
		isGloballyRated = [NSNumber numberWithBool: foundRating];
		[self setPrimitiveValue: isGloballyRated forKey: @"isGloballyRated"];
    }
	[self didAccessValueForKey: @"isGloballyRated"];
	return isGloballyRated;
}


- (NSColor *)titleColor
{
	if (_validating) return nil;
    NSColor *titleColor;
    
    [self willAccessValueForKey: @"titleColor"];
    titleColor = [self primitiveValueForKey: @"titleColor"];
    if ( nil == titleColor )
    {
        NSString *agree = [self valueForKey: @"agree"];
        titleColor = [agree isEqualToString: @"on"] ? [NSColor controlTextColor] : [NSColor redColor];
        [self setPrimitiveValue: titleColor forKey: @"titleColor"];
    }
    [self didAccessValueForKey: @"titleColor"];
    return titleColor;
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	if (context)
	{
		if ([(id)context isEqualToString: @"rating"])
		{
			[self setValue: nil forKey: @"isRated"];
			[self setValue: nil forKey: @"isGloballyRated"];
			[[self managedObjectContext] categorizeContestEntry: self];
		}
	}
}

@end
